package heapTree;

import java.util.ArrayList;

public class BinomialMinHeap
{
	private ArrayList<Object> heapArray;

	public BinomialMinHeap()
	{
		heapArray = new ArrayList<Object>();
	}

	public BinomialMinHeap(Object[] objects)
	{
		this();
		for (int i = 0; objects!=null && i < objects.length; i++)
		{
			heapArray.add(objects[i]);
		}
		//We only need to heapify the first half of the
		//heap array because the second half is all the
		//leaf nodes, which don't have any children
		//that could be larger than they are.
		for (int i = heapArray.size()/2; i >= 0; i--)
		{
			heapifyUp(i);
		}
	}

	//An object comparison function for simplifying the
	//comparisons performed in the heapify methods.
	public static boolean less(Object obj1, Object obj2)
	{
		if (((Comparable)obj1).compareTo(obj2) < 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	//This getter for the internal heap array is for
	//testing purposes.
	public ArrayList<Object> getHeapArray()
	{
		return heapArray;
	}

	public Object getMin()
	{
		if(heapArray!=null && heapArray.size()>0)
			return heapArray.get(0);
		else return null;
	}

	//Removemin swaps the root node with the last leaf node
	//to perserve heap completeness, then removes the old
	//root. heapifyDown is then called to move the new root
	//node down to correct the heap ordering. Finally, the
	//original root is returned.
	public Object removeMin()
	{
		Object min = heapArray.get(0);
		heapArray.set(0, heapArray.get(heapArray.size()-1));
		heapArray.remove(heapArray.size()-1);
		heapifyDown(0);
		return min;
	}

	//heapifyDown corrects heap ordering caused by removing the root node
	//by bubbling larger values down the heap.
	public void heapifyDown(int index)
	{
		int leftIndex = index*2 + 1;
		int rightIndex = index*2 + 2;
		//assume the current node is smaller than its children
		int indexOfSmallest = index;

		//If the current node has a left child, check if it is smaller than the current node and
		//change the smallest index if need be.
		if ((leftIndex < heapArray.size()) && (less(heapArray.get(leftIndex), heapArray.get(indexOfSmallest))))
		{
			indexOfSmallest = leftIndex;
		}

		//Ditto for the right child
		if ((rightIndex < heapArray.size()) && (less(heapArray.get(rightIndex), heapArray.get(indexOfSmallest))))
		{
			indexOfSmallest = rightIndex;
		}

		//Was one of the children smaller than the current node?
		if (indexOfSmallest != index)
		{
			//If so, swap them and recursively heapify down the correct branch of the heap.
			Object temp = heapArray.get(index);
			heapArray.set(index, heapArray.get(indexOfSmallest));
			heapArray.set(indexOfSmallest, temp);
			heapifyDown(indexOfSmallest);
		}
	}

	//Insert adds the new element to the end of the heap for completeness
	//and calls heapify on the new node's parent to get it in the correctly
	//ordered position.
	public void insert(Object obj)
	{
		heapArray.add(obj);
		heapifyUp((heapArray.size()-2)/2);
	}

	//heapifyUp corrects heap ordering caused by adding new nodes
	//by bubbling smaller values up the heap.
	public void heapifyUp(int index)
	{
		int leftIndex = index*2 + 1;
		int rightIndex = index*2 + 2;
		int indexOfSmallest = index;

		if ((leftIndex < heapArray.size()) && (less(heapArray.get(leftIndex), heapArray.get(indexOfSmallest))))
		{
			indexOfSmallest = leftIndex;
		}

		if ((rightIndex < heapArray.size()) && (less(heapArray.get(rightIndex), heapArray.get(indexOfSmallest))))
		{
			indexOfSmallest = rightIndex;
		}

		if (indexOfSmallest != index)
		{
			Object temp = heapArray.get(index);
			heapArray.set(index, heapArray.get(indexOfSmallest));
			heapArray.set(indexOfSmallest, temp);
			//heapifyUp is exactly the same as heapifyDown, except it recursively
			//works in the opposite direction.
			heapifyUp((index-1)/2);
		}
	}

	//Heapsort sorts the contents of the heap by removing the
	//minimum element until the heap is empty.
	public ArrayList Heapsort()
	{
		ArrayList sorted = new ArrayList();
		while (heapArray.size() > 0)
		{
			sorted.add(removeMin());
		}
		return sorted;
	}
}

